package ae.pcfc.cldos.online.common.manager.provider;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.net.io.Util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ae.pcfc.cldos.online.common.clddb.domain.AppMetaData;
import ae.pcfc.cldos.online.common.clddb.domain.ApplicationAttachment;
import ae.pcfc.cldos.online.common.clddb.domain.AttachmentType;
import ae.pcfc.cldos.online.common.clddb.domain.TempFileStorage;
import ae.pcfc.cldos.online.common.clddb.domain.manager.AppMetaDataDOM;
import ae.pcfc.cldos.online.common.clddb.domain.manager.ApplicationAttachmentDOM;
import ae.pcfc.cldos.online.common.clddb.domain.manager.TempFileStorageDOM;
import ae.pcfc.cldos.online.common.manager.BaseManager;
import ae.pcfc.cldos.online.common.manager.FileStorageManager;
import ae.pcfc.cldos.online.common.manager.FtpManager;
import ae.pcfc.cldos.online.common.vo.FileVO;

/**
 * 
 * @author arosa
 *
 */
public class FileStorageManagerImpl extends BaseManager implements FileStorageManager {

	@Value("${java.storage.dir}")
	private String javaStorageLocation;
	
	@Value("${gs.ftp.basedir}")
	private String dotNetStorageLocation;
	
	public enum FileStorage{
		DOT_NET_STORAGE, JAVA_STORAGE
	}
	
	private AppMetaDataDOM appMetaDataDOM;
	
	
	public AppMetaDataDOM getAppMetaDataDOM() {
		return appMetaDataDOM;
	}

	public void setAppMetaDataDOM(AppMetaDataDOM appMetaDataDOM) {
		this.appMetaDataDOM = appMetaDataDOM;
	}
	private TempFileStorageDOM tempFileStorageDOM;
	
	public void setTempFileStorageDOM(TempFileStorageDOM tempFileStorageDOM) {
		this.tempFileStorageDOM = tempFileStorageDOM;
	}
	
	private FtpManager ftpManager;
	
	public void setFtpManager(FtpManager ftpManager) {
		this.ftpManager = ftpManager;
	}
	
	private ApplicationAttachmentDOM applicationAttachmentDOM;
	
	public void setApplicationAttachmentDOM(
			ApplicationAttachmentDOM applicationAttachmentDOM) {
		this.applicationAttachmentDOM = applicationAttachmentDOM;
	}
	
	private static final Map<String, String> FILE_FOLDERS = new HashMap<String, String>(){
		/**
		 * 
		 */
		private static final long serialVersionUID = -5025982093786178005L;

		{
			put(AttachmentType.__ATTACHMENT_TYPE_PASSPORT, "PASSPORT_COPY");
			put(AttachmentType.__ATTACHMENT_TYPE_COMPANY_LETTER, "LETTER_FROM_COMPANY");
			put(AttachmentType.__ATTACHMENT_TYPE_PHOTO, "PHOTO");
			put(AttachmentType.__ATTACHMENT_TYPE_INSURRANCE_PAPER, "INSURANCE_PAPER");
			put(AttachmentType.__ATTACHMENT_TYPE_CANCELATION_OR_VISIT_PAPER, "CANCELLATION_OR_VISIT_VISA_COPY");
			put(AttachmentType.__ATTACHMENT_TYPE_CANCELATION_PAPER, "CANCELLATION_PAPER");
			put(AttachmentType.__ATTACHMENT_TYPE_EXIT_DOCUMENT, "EXIT_PAPER");
			put(AttachmentType.__ATTACHMENT_TYPE_MEDDICAL_APPOINTMENT_LET, "MEDICAL_APPOINTMENT_LETTER");
			put(AttachmentType.__ATTACHMENT_TYPE_DEBIT_NOTE, "DEBIT_NOTE");
			put(AttachmentType.__ATTACHMENT_TYPE_ENTRY_PERMIT, "ENTRY_PERMIT");
			put(AttachmentType.__ATTACHMENT_TYPE_PAYMENT_VOUCHER, "PaymentVoucher");
		}
	};
	
	public List<TempFileStorage> storeFiles(AppMetaData appMetaData, 
			List<FileVO> fileVOList, Map<String, TempFileStorage> tempFileStoragesMap) throws IOException{
		List<TempFileStorage> tempFileStorages = new ArrayList<TempFileStorage>(fileVOList.size()+1);
		for(FileVO fileVO : fileVOList){
			if(tempFileStoragesMap.containsKey(fileVO.getAttachmentTypeCode())){
				TempFileStorage tempFileStorage = tempFileStoragesMap.get(fileVO.getAttachmentTypeCode());
				deleteFromStorage(tempFileStorage.getId(), tempFileStorage.getFileExt(), 
						tempFileStorage.getAttachmentTypeCode(), FileStorage.JAVA_STORAGE);
				tempFileStorageDOM.remove(tempFileStorage);
			}
			tempFileStorages.add(storeFile(fileVO, appMetaData.getApplicationId()));
		}
		return tempFileStorages;
	}
	
	@SuppressWarnings("unchecked")
	public List<TempFileStorage> storeFiles(AppMetaData appMetaData, List<FileVO> fileVOList) throws IOException{
		return storeFiles(appMetaData, fileVOList, (Map<String, TempFileStorage>)Collections.EMPTY_MAP);
	}
	
	public TempFileStorage storeFile(FileVO fileVO, Long appId) throws IOException{
		TempFileStorage tempFileStorage = new TempFileStorage();
		mapper.map(fileVO, tempFileStorage);
		tempFileStorage.setApplicationId(appId);
		tempFileStorageDOM.add(tempFileStorage);
		writeFileToDisk(fileVO, tempFileStorage.getId(), FileStorage.JAVA_STORAGE);
		fileVO.setId(tempFileStorage.getId());
		return tempFileStorage;
	}
	
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public TempFileStorage storeFileInNewTransaction(FileVO fileVO, Long appId) throws IOException{
		return storeFile(fileVO, appId);
	}
	
	private void writeFileToDisk(FileVO fileVO, Long fileId, FileStorage fst) throws IOException{
		String fileLocation = null;
		InputStream is = fileVO.getInputStream();
		if(fst.equals(FileStorage.JAVA_STORAGE)){
			fileLocation = javaStorageLocation.endsWith("/")?javaStorageLocation : javaStorageLocation+"/";
		
			File baseDir = new File(fileLocation);
			if(!baseDir.exists() || !baseDir.isDirectory()){
				baseDir.mkdir();
			}
			fileLocation += FILE_FOLDERS.get(fileVO.getAttachmentTypeCode())+"/";
			File typeDir = new File(fileLocation);
			if(!typeDir.exists() || !typeDir.isDirectory()){
				typeDir.mkdir();
			}
			fileLocation += fileId.toString() + "." + fileVO.getFileExt();
			File fileToStore = new File(fileLocation);
			if(fileToStore.exists()){
				fileToStore.delete();
			}
			fileToStore.createNewFile();
			OutputStream os = new FileOutputStream(fileToStore);
			Util.copyStream(is, os);
			os.close();
		} else {
			fileLocation = getFileLocation(fileId, fileVO.getFileExt(),
					fileVO.getAttachmentTypeCode(), FileStorage.DOT_NET_STORAGE,fileVO.getApplicationId());
			if(ftpManager.exist(fileLocation)){
				ftpManager.remove(fileLocation);
			}
			ftpManager.writeFile(fileLocation, is);
		}
	}
	
	public FileVO getFileFromJavaStorageById(Long fileId) throws FileNotFoundException{
		TempFileStorage tempFileStorage = tempFileStorageDOM.findById(fileId);
		File f = new File(getFileLocation(fileId, tempFileStorage.getFileExt(), 
				tempFileStorage.getAttachmentTypeCode(), FileStorage.JAVA_STORAGE,null));
		if(!f.exists()){
			throw new FileNotFoundException();
		}
		FileVO fileVO = new FileVO();
		mapper.map(tempFileStorage, fileVO);
		FileInputStream fis = new FileInputStream(f);
		fileVO.setInputStream(fis);
		fileVO.setContentLength(f.length());
		return fileVO;
	}

	public FileVO getFileFromGSStorageById(Long fileId) throws FileNotFoundException{
		ApplicationAttachment applicationAttachment = applicationAttachmentDOM.findById(fileId);
		String fileLocation = getFileLocation(fileId, applicationAttachment.getAttachmentFileExt(), 
				applicationAttachment.getAttachmentTypeCode(), FileStorage.DOT_NET_STORAGE,applicationAttachment.getAppId());
		FileVO fileVO = new FileVO();
		mapper.map(applicationAttachment, fileVO);
		fileVO.setInputStream(ftpManager.getFile(fileLocation));
		return fileVO;
	}
	
	private String getFileLocation(Long fileId, String fileExt, String fileType, FileStorage fst,Long appId){
		String fileLocation = null;
		TempFileStorage tempFileStorage = tempFileStorageDOM.findById(fileId);
		AppMetaData appMetaData = new AppMetaData();
		if(fst.equals(FileStorage.JAVA_STORAGE)){		
			fileLocation = javaStorageLocation.endsWith("/")?javaStorageLocation : javaStorageLocation+"/";
		} else {
			fileLocation = dotNetStorageLocation.endsWith("/")?dotNetStorageLocation : dotNetStorageLocation+"/";
		}
		fileLocation += FILE_FOLDERS.get(fileType)+"/";
		if(!fst.equals(FileStorage.JAVA_STORAGE) && fileType.equals("30")){
			try {
				if(tempFileStorage != null && tempFileStorage.getApplicationId() != null){
					appMetaData= appMetaDataDOM.findAppMetaDataById(tempFileStorage.getApplicationId());
				}else{
					appMetaData= appMetaDataDOM.findAppMetaDataById(appId);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			if(appMetaData.getClientRefNo()!=null){
				fileLocation += appMetaData.getClientRefNo()+"_"+fileId.toString() + "." + fileExt;
			}else{
				fileLocation += fileId.toString() + "." + fileExt;
			}
		}else{
			fileLocation += fileId.toString() + "." + fileExt;
		}
		return fileLocation;
	}
	
	public void deletePaymentVouchers(Long appId){
		List<TempFileStorage> paymentVouchers = tempFileStorageDOM.getPaymentVouchers(appId);
		List<Long> ids = new ArrayList<Long>();
		for (Iterator<TempFileStorage> iterator = paymentVouchers.iterator(); iterator.hasNext();) {
			TempFileStorage tempFileStorage = (TempFileStorage) iterator.next();
			deleteFromStorage(tempFileStorage.getId(), tempFileStorage.getFileExt(), 
					tempFileStorage.getAttachmentTypeCode(), FileStorage.JAVA_STORAGE);
			ids.add(tempFileStorage.getId());
		}
		tempFileStorageDOM.deleteFileRecordsByIds(ids);
	}
	
	public void deleteFromStorage(Long fileId, String fileExt, String fileType, FileStorage fst){
		String fileLocation = getFileLocation(fileId, fileExt, fileType, fst,null);
		if(fst==FileStorage.DOT_NET_STORAGE){
			try {
				if(ftpManager.exist(fileLocation)){
					ftpManager.remove(fileLocation);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else {
			File f = new File(fileLocation);
			if(f.exists()){
				f.delete();
			}
		}
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public Long transferToDotNet(FileVO fileVO) throws IOException {
		String fileLocation = getFileLocation(fileVO.getId(), fileVO.getFileExt(), 
				fileVO.getAttachmentTypeCode(), FileStorage.JAVA_STORAGE,null);
		File javaFile = new File(fileLocation);
		if(!javaFile.exists()){
			throw new FileNotFoundException(fileVO.toString());
		}
		InputStream is = new FileInputStream(javaFile);
		fileVO.setInputStream(is);
		
		ApplicationAttachment applicationAttachment = new ApplicationAttachment();
		mapper.map(fileVO, applicationAttachment);
		applicationAttachmentDOM.add(applicationAttachment);
		
		writeFileToDisk(fileVO, applicationAttachment.getAttachmentId(), FileStorage.DOT_NET_STORAGE);
		
		return applicationAttachment.getAttachmentId();
	}
}
